package com.markyao.processor;

import com.markyao.recos.raft.core.RaftCore;
import com.markyao.recos.raft.raftRole.AbstractRaftRole;
import com.markyao.recos.raft.raftRole.RaftRoleImpl;
import com.markyao.recos.regist.RecosRegistConfig;
import com.markyao.utils.HostUtils;
import com.markyao.utils.MD5Util;
import com.markyao.utils.PropertySourcesUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.atomic.AtomicInteger;

@Slf4j(topic = "m.EnviromentProcessor")
@Component
public class EnviromentProcessor implements BeanFactoryPostProcessor, EnvironmentAware , BeanFactoryAware {
    private DefaultListableBeanFactory beanFactory;
    private ConfigurableEnvironment environment;
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        RecosRegistConfig recosRegistConfig = readRecosConfig(this.environment);
        if (recosRegistConfig==null){
            throw new BeanCreationException("没有recosRegistConfig，请检查recos配置!");
        }
        configurableListableBeanFactory.registerSingleton("recosRegistConfig",recosRegistConfig);
        if (recosRegistConfig.isGroup()){

            AtomicInteger isElection = (AtomicInteger) this.beanFactory.getBean("isElection");
            RestTemplate restTemplate = this.beanFactory.getBean(RestTemplate.class);

            int tellThreshold=recosRegistConfig.getGroupAds().length;
            AtomicInteger recvThreshold=new AtomicInteger(tellThreshold);
            AtomicInteger sendThreshold=new AtomicInteger(tellThreshold);
            registSpringBean("recvThreshold",AtomicInteger.class,recvThreshold);
            registSpringBean("sendThreshold",AtomicInteger.class,sendThreshold);

            AbstractRaftRole raftRole=new RaftRoleImpl(recosRegistConfig.getHost(), recosRegistConfig.getPort(),
                    recosRegistConfig.getUsername(), recosRegistConfig.getPassword(),
                    restTemplate,isElection,sendThreshold,recvThreshold,recosRegistConfig);
            raftRole.setRoleState(1);
            raftRole.setGroupsAds(recosRegistConfig.getGroupAds());
            raftRole.setScore(new AtomicInteger(0));
            raftRole.setVote(false);
            raftRole.setRaftRoleList(null);
            int size = tellThreshold+1;
            raftRole.setSafeScore((size/2)+1);
            registSpringBean("raftRole",AbstractRaftRole.class,raftRole);
        }else {
            log.info("recos start on standalone.");
            this.beanFactory.removeBeanDefinition("raftCore");
            this.beanFactory.removeBeanDefinition("raftController");
            this.beanFactory.removeBeanDefinition("recosGroupController");
        }
    }

    private void registSpringBean(String beanName,Class clazz,Object bean) {
        GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
        genericBeanDefinition.setBeanClass(clazz);
        this.beanFactory.registerBeanDefinition(beanName,genericBeanDefinition);
        if (bean!=null){
            this.beanFactory.registerSingleton(beanName, bean);
        }
    }


    @Override
    public void setEnvironment(Environment environment) {
        this.environment= (ConfigurableEnvironment) environment;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory= (DefaultListableBeanFactory) beanFactory;

    }


    private RecosRegistConfig readRecosConfig(ConfigurableEnvironment environment){
        RecosRegistConfig recosRegistryConfig=null;
        try {
            MutablePropertySources propertySources = environment.getPropertySources();
            for (PropertySource<?> propertySource : propertySources) {
                if (PropertySourcesUtils.isApplicationConf(propertySource)){
                    String key = "server";
                    int port = (int) propertySource.getProperty(key + ".port");
                    String host= HostUtils.getThisHost();
                    String username = propertySource.getProperty(key + ".username").toString();
                    String password = propertySource.getProperty(key + ".password").toString();
                    key=key+".group";
                    Boolean enabled = (Boolean) propertySource.getProperty(key + ".enabled");
                    if (enabled != null && enabled) {
                        String addressestr=  propertySource.getProperty(key+".address").toString();
                        String [] ads= addressestr.split(",");
                        recosRegistryConfig=new RecosRegistConfig(host,port,ads);
                        recosRegistryConfig.setGroup(true);
                    }else {
                        recosRegistryConfig=new RecosRegistConfig(host,port,null);
                        recosRegistryConfig.setGroup(false);
                    }
                    recosRegistryConfig.setUsername(username);
                    recosRegistryConfig.setPassword(password);

                    recosRegistryConfig.setUsernameMD5(MD5Util.string2MD5(username));
                    recosRegistryConfig.setPasswordMD5(MD5Util.string2MD5(password));
                    return recosRegistryConfig;
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
